package com.wang.springbootinit.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wang.springbootinit.common.*;
import com.wang.springbootinit.constant.BISystemEnum;
import com.wang.springbootinit.constant.CommonConstant;
import com.wang.springbootinit.constant.UserConstant;
import com.wang.springbootinit.exception.BusinessException;
import com.wang.springbootinit.exception.ThrowUtils;
import com.wang.springbootinit.mapper.UserMapper;
//import com.wang.springbootinit.minio.MinioUtil;
import com.wang.springbootinit.model.dto.user.UserQueryRequest;
import com.wang.springbootinit.model.dto.user.UserUpdateRequest;
import com.wang.springbootinit.model.entity.SparkInfo;
import com.wang.springbootinit.model.entity.User;
import com.wang.springbootinit.model.enums.UserRoleEnum;
import com.wang.springbootinit.model.vo.LoginUserVO;
import com.wang.springbootinit.model.vo.UserExtendInfoVO;
import com.wang.springbootinit.model.vo.UserInfoVO;
import com.wang.springbootinit.model.vo.UserVO;
import com.wang.springbootinit.service.SparkInfoService;
import com.wang.springbootinit.service.TokenComponent;
import com.wang.springbootinit.service.UserService;
import com.wang.springbootinit.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.wang.springbootinit.constant.UserConstant.USER_LOGIN_STATE;

/**
 * 用户服务实现
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

    @Resource
    private SparkInfoService sparkInfoService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private TokenComponent tokenComponent;
    /**
     * 盐值，混淆密码
     */
    private static final String SALT = "wang";

    /**
     * 查询用户首页信息
     *
     * @param request
     * @return
     */
    @Override
    public UserExtendInfoVO getUserExtendInfo(HttpServletRequest request) {
        UserExtendInfoVO userExtendInfoVO = new UserExtendInfoVO();
        try {
            User user = this.getUser(request);
            if (ObjectUtil.isNull(user)) {
                throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
            }

            QueryWrapper<SparkInfo> sparkInfoQueryWrapper = new QueryWrapper<>();
            sparkInfoQueryWrapper.eq("userId", user.getId());
            List<SparkInfo> sparkInfoList = sparkInfoService.list(sparkInfoQueryWrapper);
            if (CollectionUtil.isNotEmpty(sparkInfoList)) {
                LocalDate today = LocalDate.now(); //
                List<SparkInfo> todaySparkInfoList = sparkInfoList.stream()
                        .filter(item -> isSameDay(item.getCreateTime(), today))
                        .collect(Collectors.toList());
                userExtendInfoVO.setCurrentDateInvokeCount(todaySparkInfoList.size());
            }


            userExtendInfoVO.setUserId(user.getId());
            userExtendInfoVO.setUserName(user.getUserName());
            userExtendInfoVO.setInvokeCount(user.getInvokeCount());

            return userExtendInfoVO;
        } catch (Exception e) {
            LOGGER.error("getUserExtendInfo exception:", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 是否是相同的一天
     *
     * @param date
     * @param localDate
     * @return
     */
    private static boolean isSameDay(Date date, LocalDate localDate) {
        // 将Date类型转换为LocalDate类型进行比较
        return localDate.isEqual(date.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate());
    }

    @Override
    public UserInfoVO getUser(Long id, HttpServletRequest request) {
        User user = new User();
        if (ObjectUtil.isNull(id)) {
            //返回当前登录的用户
            if (ObjectUtil.isNotNull(request)) {
                user = this.getUser(request);
            }
        } else {
            user = this.getById(id);
        }

        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(user, userInfoVO);
        userInfoVO.setUserRole(user.getUserRole());

        return userInfoVO;
    }

    /**
     * 获取当前登录用户
     *
     * @param request
     * @return
     */
    @Override
    public User getUser(HttpServletRequest request) {
        return this.getLoginUser(request);
    }

    /**
     * 更新用户信息
     *
     * @param userUpdateRequest
     * @return
     */
    @Override
    public Boolean updateUser(UserUpdateRequest userUpdateRequest) {
        //判断邮箱是否已经有人用了
        if (StrUtil.isNotEmpty(userUpdateRequest.getEmail())) {
            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
            userQueryWrapper.eq("email", userUpdateRequest.getEmail());
            List<User> userList = this.list(userQueryWrapper);
            if (userList.size() > 1) {
                throw new BusinessException(ErrorCode.THE_MAILBOX_HAS_BEEN_USED);
            }
        }
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper.eq("id", userUpdateRequest.getId());
        userUpdateWrapper.set(StrUtil.isNotEmpty(userUpdateRequest.getUserName()), "userName", userUpdateRequest.getUserName());
        userUpdateWrapper.set(ObjectUtil.isNotNull(userUpdateRequest.getSex()), "sex", userUpdateRequest.getSex());
        userUpdateWrapper.set(StrUtil.isNotEmpty(userUpdateRequest.getUserProfile()), "userProfile", userUpdateRequest.getUserProfile());
        userUpdateWrapper.set(StrUtil.isNotEmpty(userUpdateRequest.getEmail()), "email", userUpdateRequest.getEmail());
        boolean result = this.update(userUpdateWrapper);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return Boolean.TRUE;
    }

    /**
     * 更新用户头像
     *
     * @param file
     * @param userUpdateRequest
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateUserAvatar(MultipartFile file, UserUpdateRequest userUpdateRequest) {
        if (ObjectUtil.isNull(userUpdateRequest.getId())) {
            LOGGER.error("updateUserAvatar userUpdateRequest:{}", userUpdateRequest);
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = new User();
        String userAvatarPre = user.getUserAvatar(); //原头像地址
        user.setId(userUpdateRequest.getId());
        if (ObjectUtil.isNotNull(file)) {
            CommonUtils.validFile(file);
//            List<String> fileNameList = minioUtil.upload(file, FileEnum.AVATAR.getBucketName());
//            if (CollectionUtil.isNotEmpty(fileNameList) && fileNameList.size() == 2) {
//                user.setUserAvatar(fileNameList.get(1));
//                if (StrUtil.isNotEmpty(userAvatarPre)) {
//                    //删除原头像文件
//                    minioUtil.remove(user.getUserAvatar());
//                }
//            }
        }
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper.eq("id", user.getId());
        userUpdateWrapper.set("userAvatar", user.getUserAvatar());
        boolean result = this.update(userUpdateWrapper);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return Boolean.TRUE;
    }

    @Override
    public Boolean removeUser(DeleteRequest deleteRequest) {
        try {
            User user = this.getById(deleteRequest.getId());
//            minioUtil.remove(user.getUserAvatar());
            return this.removeById(deleteRequest.getId());
        } catch (Exception e) {
            log.error("UserServiceImpl removeUser e:", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 分页查询用户信息
     *
     * @param request
     * @return
     */
    @Override
    public BaseResponsePage<List<UserVO>> listUserPage(UserQueryRequest param,HttpServletRequest request) {
        try {
            long current = param.getCurrent();
            long size = param.getPageSize();

            User loginUser = this.getLoginUser(request);
            if(ObjectUtil.isNull(loginUser)){
                throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
            }

            Page<User> userPage = this.page(new Page<>(current, size),
                    this.getQueryWrapper(param,loginUser));
            if (ObjectUtil.isNull(userPage) || CollectionUtil.isEmpty(userPage.getRecords())) {
                return new BaseResponsePage<>(BISystemEnum.SUCCESS.getCode(), new ArrayList<>(), "请求成功");
            }
            List<UserVO> userVOList = userPage.getRecords().stream().map(x -> {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(x,userVO);
                if(ObjectUtil.isNotNull(x.getUserRole())){
                    userVO.setUserRole(UserRoleEnum.getTextByValue(x.getUserRole()));
                }
                return userVO;
            }).collect(Collectors.toList());
            return new BaseResponsePage<>(BISystemEnum.SUCCESS.getCode(), userVOList, "请求成功", (int) userPage.getCurrent(), (int) userPage.getSize(), (int) userPage.getTotal());
        } catch (Exception e) {
            log.error("UserServiceImpl listUserPage exception:", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    //用户注册
    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        synchronized ((Object) userAccount.hashCode()) {
            // 账户不能重复
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userAccount", userAccount);
            long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
            }
            // 2. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            // 3. 插入数据
            User user = new User();
            user.setUserAccount(userAccount);
            user.setUserPassword(encryptPassword);
            user.setInvokeCount(UserConstant.DefaultInvokeCount);
            user.setSex(0);
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
            }

            //返回用户id
            return user.getId();
        }
    }

    //用户登录
    @Override
    public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号格式错误");
        }
        if (userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码格式错误");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        User tempUser = this.baseMapper.selectOne(queryWrapper);
        if(ObjectUtil.isNull(tempUser)){
            throw new BusinessException(ErrorCode.ACCOUNT_DOES_NOT_EXIST);
        }
        queryWrapper.eq("userPassword", encryptPassword);
        User user = this.baseMapper.selectOne(queryWrapper);
        // 用户不存在
        if (ObjectUtil.isNull(user)) {
            log.info("user login failed, userAccount cannot match userPassword");
            throw new BusinessException(ErrorCode.WRONG_PASSWORD);
        }
        //黑名单
        if(user.getUserRole().equals(UserConstant.BAN_ROLE)){
            throw new BusinessException(ErrorCode.BLACKLIST_USER);
        }

        // 3. 记录用户的登录态 redis session
//        request.getSession().setAttribute(USER_LOGIN_STATE, user);
        LoginUserVO loginUserVO = this.getLoginUserVO(user);
        String token = tokenComponent.createToken(loginUserVO);
        loginUserVO.setToken(token);
        return loginUserVO;
    }


    /**
     * 获取当前登录用户
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        LoginUserVO currentUser = tokenComponent.getLoginUser(request);
        // 先判断是否已登录
//        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
//        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        User user = this.getById(userId);
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return user;
    }

    /**
     * 获取当前登录用户（允许未登录）
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUserPermitNull(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            return null;
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        return this.getById(userId);
    }

    /**
     * 是否为管理员
     *
     * @param request
     * @return
     */
    @Override
    public boolean isAdmin(HttpServletRequest request) {
        // 仅管理员可查询
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User user = (User) userObj;
        return isAdmin(user);
    }

    @Override
    public boolean isAdmin(User user) {
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }

    /**
     * 用户注销
     *
     * @param request
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        tokenComponent.delLoginUser(null,request);
//        if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
//            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
//        }
        // 移除登录态
//        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return true;
    }

    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtils.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    public List<UserVO> getUserVO(List<User> userList) {
        if (CollectionUtils.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String userName = userQueryRequest.getUserName();
        String userProfile = userQueryRequest.getUserProfile();
        Integer userRole = userQueryRequest.getUserRole();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(id != null, "id", id);
        queryWrapper.eq(ObjectUtil.isNotNull(userRole), "userRole", userRole);
        queryWrapper.like(StringUtils.isNotBlank(userProfile), "userProfile", userProfile);
        queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest,User user) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String userName = userQueryRequest.getUserName();
        Integer sex = userQueryRequest.getSex();
        String userProfile = userQueryRequest.getUserProfile();
        Integer userRole = userQueryRequest.getUserRole();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if(ObjectUtil.isNotNull(user) && user.getUserRole().equals(UserConstant.DEFAULT_ROLE)){
            queryWrapper.eq( "id", user.getId());
        }
        queryWrapper.eq(ObjectUtil.isNotNull(userRole), "userRole", userRole);
        queryWrapper.eq(ObjectUtil.isNotNull(sex), "sex", sex);
        queryWrapper.like(StringUtils.isNotBlank(userProfile), "userProfile", userProfile);
        queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 设置角色权限
     *
     * @param param
     * @param request
     * @return
     */
    @Override
    public Boolean setUserPermission(UserUpdateRequest param, HttpServletRequest request) {
        log.error("UserServiceImpl setUserPermission param:{}", JSONUtil.toJsonStr(param));
        Integer userRole = param.getUserRole();
        if (ObjectUtil.isNull(userRole) || ObjectUtil.isNull(param.getId())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        try {
            User operateUser = this.getById(param.getId());
            operateUser.setUserRole(userRole);
            this.updateById(operateUser);
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("UserServiceImpl setUserPermission exception:", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 忘记密码
     *
     * @param param
     * @return
     */
    @Override
    public Boolean forgotPassword(UserUpdateRequest param) {
        String paramEmail = param.getEmail();
        String code = param.getVerificationCode();
        LOGGER.info("UserServiceImpl forgotPassword paramEmail:{}, code:{}", paramEmail, code);
        try {
            if (ObjectUtil.isNull(paramEmail) || ObjectUtil.isNull(code)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            if (param.getNewPassword().length() < 8) {
                LOGGER.error("forgotPassword newPassword:{}", param.getNewPassword());
                throw new BusinessException(ErrorCode.PASSWORD_IS_INVALID);
            }
            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
            userQueryWrapper.eq("email", paramEmail);
            List<User> userList = this.list(userQueryWrapper);
            if (userList.size() == 0) {
                LOGGER.error("forgotPassword userList:{}", JSONUtil.toJsonStr(userList));
                throw new BusinessException(ErrorCode.EMAIL_NOT_REGISTERED);
            }

            String cacheCode = redisTemplate.opsForValue().get(UserConstant.userSendCode + paramEmail);
            if (!code.equals(cacheCode)) {
                LOGGER.error("forgotPassword cacheCode:{}", cacheCode);
                throw new BusinessException(ErrorCode.VERIFICATION_CODE_ERROR);
            }

            //校验通过
            User user = userList.get(0);
            UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
            userUpdateWrapper.eq("id", user.getId());
            String digestPassword = DigestUtils.md5DigestAsHex((SALT + param.getNewPassword()).getBytes());
            userUpdateWrapper.set("userPassword", digestPassword);
            this.update(userUpdateWrapper);

            //删除缓存
            redisTemplate.delete(UserConstant.userSendCode + paramEmail);
            return Boolean.TRUE;
        } catch (Exception e) {
            LOGGER.error("UserServiceImpl forgotPassword exception:", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    @Override
    public Boolean sendCode(UserUpdateRequest param) {
        String paramEmail = param.getEmail();
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("email", paramEmail);
        List<User> userList = this.list(userQueryWrapper);
        if (userList.size() == 0) {
            LOGGER.error("forgotPassword userList:{}", JSONUtil.toJsonStr(userList));
            throw new BusinessException(ErrorCode.EMAIL_NOT_REGISTERED);
        }

        String code = RandomUtil.randomNumbers(6);

        //缓存
        String redisKey = UserConstant.userSendCode + paramEmail;
        redisTemplate.opsForValue().set(redisKey, code, 3, TimeUnit.MINUTES); //三分钟

        return Boolean.TRUE;

    }
}
